﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Identity;
using Eqwww.Domain.Models.Identity;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
using Eqwww.Data;
using System.Linq;
using System.Threading.Tasks;
using Eqwww.Code;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;
using Eqwww.Domain.Models.SystemManage;
using Eqwww.Domain;
using Eqwww.App.SystemManage;

namespace Eqwww.App.Identity
{
    public class UserApp : UserManager<UserModel>
    {
        private ApplicationDbContext _context;
        private RoleManager<RoleModel> _roleManager;
        private IModelHandler _modelHandler;
        private DatabaseLogApp _databaseLogApp;

        public UserApp(IUserStore<UserModel> userStore
            , IOptions<IdentityOptions> options
            , IPasswordHasher<UserModel> passwordHasher
            , IEnumerable<IUserValidator<UserModel>> userValidators
            , IEnumerable<IPasswordValidator<UserModel>> passwordValidators
            , ILookupNormalizer lookupNormalizer
            , IdentityErrorDescriber identityErrorDescriber
            , IServiceProvider serviceProvider
            , ILogger<UserManager<UserModel>> logger
            , ApplicationDbContext context
            , RoleManager<RoleModel> roleManager
            ,IModelHandler modelHandler
            ,DatabaseLogApp databaseLogApp) : base(userStore,
                options,
                passwordHasher,
                userValidators,
                passwordValidators,
                lookupNormalizer,
                identityErrorDescriber,
                serviceProvider,
                logger)
        {
            _context = context;
            _roleManager = roleManager;
            _modelHandler = modelHandler;
            _databaseLogApp = databaseLogApp;
        }

        public IQueryable<UserModel> GetQueryable()
        {
            return Users.Where(x => x.DeleteMark != true);
        }

        
        public ModelResult Create(UserModel model, string password)
        {
            _modelHandler.Create(model);
            var result =  CreateAsync(model, password).Result;
            if (!result.Succeeded)
            {
                var sb = new System.Text.StringBuilder();
                foreach (var err in result.Errors)
                {
                    sb.Append(err.Description + "(" + err.Code + ")<br />");
                }
                return Common.ErrorResult(sb.ToString());
            }
            else
            {
                _databaseLogApp.LogCreateInformation(model.Id, model, "创建用户[" + model.UserName + "]");
                return Common.SuccessResult();
            }

        }
        

        ///提级或降级
        public ModelResult UpdateIsSystem(string userId, bool issystem)
        {
            var user = FindByIdAsync(userId).Result;
            if (user == null)
                return Common.ErrorResult("未找到指定用户");

            user.IsSystem = issystem;
            var result = UpdateAsync(user).Result;
            if (result.Succeeded)
                return Common.SuccessResult();
            else
            {
                return Common.ErrorResult("未知错误");
            }
        }

        public string GetAvatar(UserModel model)
        {
            if (string.IsNullOrEmpty(model.Avatar))
            {
                return "/images/avatars/default.png";
            }
            else
            {
                return model.Avatar;
            }
        }

       

        

        /// <summary>
        /// 添加用户到指定角色列表，添加前清除用户角色信息
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="roleIds"></param>
        /// <returns></returns>
        public ModelResult AddToRoles(string userId, string[] roleIds)
        {
            var user = FindByIdAsync(userId).Result;
            if (user == null)
                return Common.ErrorResult("未找到指定用户");
            if (user.DeleteMark == true)
                return Common.ErrorResult("用户已被移除");
            var needRemove = GetRolesAsync(user).Result;
            var needAdds = (from p in _roleManager.Roles.AsNoTracking().Where(x => roleIds.Contains(x.Id) && x.DeleteMark != true) select p.Name).ToList();
            var removeResult = RemoveFromRolesAsync(user, needRemove).Result;
            if (removeResult.Succeeded)
            {
                var addResult = AddToRolesAsync(user, needAdds).Result;
                if (addResult.Succeeded)
                {
                    return Common.SuccessResult();
                }
                else
                {
                    var sb = new StringBuilder();
                    foreach (var err in addResult.Errors)
                    {
                        sb.Append(err.Description + "(" + err.Code + ")<br />");
                    }
                    return Common.ErrorResult(sb.ToString());
                }
            }
            else
            {
                var sb = new StringBuilder();
                foreach (var err in removeResult.Errors)
                {
                    sb.Append(err.Description + "(" + err.Code + ")<br />");
                }
                return Common.ErrorResult(sb.ToString());
            }
        }

        /// <summary>
        /// 添加用户到指定岗位，添加前清除用户岗位。
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="postIds"></param>
        /// <returns></returns>
        public ModelResult AddToPosts(string userId, string[] postIds)
        {
            var user = FindByIdAsync(userId).Result;
            if (user == null)
                return Common.ErrorResult("未找到指定用户");
            var userPosts = _context.Sys_UserPosts.Where(x => x.UserId == userId);
            _context.Sys_UserPosts.RemoveRange(userPosts);
            var adds = from p in postIds
                       select new UserPostModel
                       {
                           Id=Guid.NewGuid().ToString(),
                           UserId = userId,
                           PostId = p
                       };

            _context.Sys_UserPosts.AddRange(adds);
            _context.SaveChanges();
            return Common.SuccessResult();
        }

        public ModelResult UpdateIsDisabled(string userId, bool isDisabled)
        {
            var user = FindByIdAsync(userId).Result;
            if (user == null)
                return Common.ErrorResult("未找到指定用户");

            user.IsDisabled=isDisabled;
            var result=UpdateAsync(user).Result;
            if(result.Succeeded)
            {
                return Common.SuccessResult();
            }
            else{
                var sb=new StringBuilder();
                foreach(var err in result.Errors)
                {
                    sb.Append(err.Description+"<br>");
                }
                return Common.ErrorResult(sb.ToString());
            }
        }

        /// <summary>
        /// 根据用户名获取关联角色列表
        /// </summary>
        /// <param name="userid"></param>
        /// <returns></returns>
        public List<RoleModel> GetRolesByUserId(string userid)
        {
            var user = FindByIdAsync(userid).Result;
            if (user == null)
                return null;
            var roleNames = GetRolesAsync(user).Result;
            var roles = _roleManager.Roles.AsNoTracking().Where(x => x.DeleteMark != true && roleNames.Contains(x.Name)).OrderBy(x => x.SortCode);
            return roles.ToList();
        }

        /// <summary>
        /// 根据用户名获取关联角色列表
        /// </summary>
        /// <param name="userid"></param>
        /// <returns></returns>
        public List<RoleModel> GetRolesByPrincipal(System.Security.Claims.ClaimsPrincipal principal)
        {
            var user = GetUserAsync(principal).Result;
            if (user == null)
                return null;
            var roleNames = GetRolesAsync(user).Result;
            var roles = _roleManager.Roles.AsNoTracking().Where(x => x.DeleteMark != true && roleNames.Contains(x.Name)).OrderBy(x => x.SortCode);
            return roles.ToList();
        }

        /// <summary>
        /// 根据用户名获取关联岗位列表
        /// </summary>
        /// <param name="userid"></param>
        /// <returns></returns>
        public List<PostModel> GetPostsByPrincipal(string userId)
        {
            var user = FindByIdAsync(userId).Result;
            if (user == null)
                return null;

            var postIds = from p in _context.Sys_UserPosts.AsNoTracking().Where(x => x.UserId == user.Id)
                          select p.PostId;

            var data = _context.Sys_Posts.AsNoTracking().Where(x => x.DeleteMark != true && postIds.Contains(x.Id)).OrderBy(x => x.SortCode);
            return data.ToList();
        }

        /// <summary>
        /// 查看指定角色是否具有指定编码的按钮声明权限
        /// </summary>
        /// <param name="principal"></param>
        /// <param name="claimCode"></param>
        /// <returns></returns>
        public bool IsUserHasButtonClaim(ClaimsPrincipal principal, string claimCode)
        {
            if (!principal.Identity.IsAuthenticated)
                return false;

            var user = GetUserAsync(principal).Result;
            if (user == null)
                return false;

            var buttonClaim = _context.Sys_ModuleButtonClaims.AsNoTracking().FirstOrDefault(x => x.Code == claimCode);
            if (buttonClaim == null)
                return false;

            var roleNames = GetRolesAsync(user).Result;

            var roleIds = from p in _roleManager.Roles.AsNoTracking().Where(x => x.DeleteMark != true && roleNames.Contains(x.Name)) select p.Id;
            var strategorys = from p in _context.Sys_StrategoryRoles.AsNoTracking()
                              join q in _context.Sys_Strategorys.AsNoTracking()
                              on p.StrategoryId equals q.Id
                              where roleIds.Contains(p.RoleId) && q.DeleteMark != true
                              select q;

            if (strategorys.Any(x => x.IsSuper == true))
                return true;

            var strategoryIds = from p in strategorys select p.Id;

            var claimIds = from p in _context.Sys_StrategoryModuleButtonClaims.AsNoTracking()
                           join q in _context.Sys_ModuleButtonClaims.AsNoTracking() on p.ClaimId equals q.Id
                           where strategoryIds.Contains(p.StrategoryId) && q.DeleteMark != true
                           select q.Id;

            if (claimIds.Contains(buttonClaim.Id))
                return true;

            return false;
        }


        /// <summary>
        /// 根据用户id获取关联策略列表
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public List<StrategoryModel> GetStrategorysByUser(string userId)
        {
            var user = FindByIdAsync(userId).Result;
            if (user == null)
                return null;
            var roleNames = GetRolesAsync(user).Result;
            var roleIds = from p in _roleManager.Roles.AsNoTracking().Where(x => x.DeleteMark != true && roleNames.Contains(x.Name)).OrderBy(x => x.SortCode)
                          select p.Id;

            var strategoryIds = (from p in _context.Sys_StrategoryRoles.AsNoTracking().Where(x => roleIds.Contains(x.RoleId))
                                 select p.StrategoryId).Distinct();

            var strategorys = _context.Sys_Strategorys.AsNoTracking().Where(x => x.DeleteMark != true && strategoryIds.Contains(x.Id)).OrderBy(x => x.SortCode);
            return strategorys.ToList();
        }

        public bool IsUserInSuper(string userId)
        {
            var strategorys = GetStrategorysByUser(userId);
            if (strategorys == null)
                return false;
            if (strategorys.Any(x => x.IsSuper == true))
                return true;
            return false;
        }
        public List<TreeCollapseToggleModel> GetRoleTreeCollapseToggleItemsByUserId(string userId)
        {
            var user = FindByIdAsync(userId).Result;

            var data = (from p in _roleManager.Roles.Where(x => x.DeleteMark != true).OrderBy(x => x.SortCode)
                        select new TreeCollapseToggleModel
                        {
                            Id = p.Id,
                            CanToggle = true,
                            Disabled = false,
                            Expend = true,
                            ParentId = null,
                            SortCode = p.SortCode,
                            Text = p.ShowName,
                            Url = p.Description,
                            ToggleMark = false
                        }).ToList();

            foreach (var item in data)
            {
                var role = _roleManager.FindByIdAsync(item.Id).Result;
                item.ToggleMark = user == null ? false : IsInRoleAsync(user, role.Name).Result;
            }

            return data.ToList();
        }

        public List<TreeCollapseToggleModel> GetPermissionsByUser(string userId)
        {
            var user = FindByIdAsync(userId).Result;
            if (user == null)
                return null;
            var roleNames = GetRolesAsync(user).Result;
            var roleIds = from p in _roleManager.Roles.AsNoTracking().Where(x => x.DeleteMark != true && roleNames.Contains(x.Name)).OrderBy(x => x.SortCode)
                          select p.Id;

            var strategoryIds = (from p in _context.Sys_StrategoryRoles.AsNoTracking().Where(x => roleIds.Contains(x.RoleId))
                                 select p.StrategoryId).Distinct();

            var strategorys = _context.Sys_Strategorys.AsNoTracking().Where(x => x.DeleteMark != true && strategoryIds.Contains(x.Id)).OrderBy(x => x.SortCode);
            var trueStrategoryIds = from p in strategorys select p.Id;

            var authorizedModuleIds = (from p in _context.Sys_StrategoryModules.Where(x => trueStrategoryIds.Contains(x.StrategoryId))
                                       select p.ModuleId).Distinct();

            var authorizedButtonIds = (from p in _context.Sys_StrategoryModuleButtons.Where(x => trueStrategoryIds.Contains(x.StrategoryId))
                                       select p.ModuleButtonId).Distinct();

            var authorizedModuleClaimIds = (from p in _context.Sys_StrategoryModuleClaims.Where(x => trueStrategoryIds.Contains(x.StrategoryId))
                                            select p.ClaimId).Distinct();

            var authorizedButtonClaimIds = (from p in _context.Sys_StrategoryModuleButtonClaims.Where(x => trueStrategoryIds.Contains(x.StrategoryId))
                                            select p.ClaimId).Distinct();

            var modules = from p in _context.Sys_Modules.AsNoTracking().Where(x => x.DeleteMark != true)
                          select new TreeCollapseToggleModel
                          {
                              Id = p.Id,
                              Text = p.Name,
                              Url = p.Url,
                              ParentId = p.ParentId,
                              SortCode = p.SortCode,
                              Icon = "list",
                              CanToggle = true,
                              Disabled = false,
                              Expend = true,
                              ToggleMark = authorizedModuleIds.Contains(p.Id)
                          };

            var buttons = from p in _context.Sys_ModuleButtons.AsNoTracking().Where(x => x.DeleteMark != true)
                          select new TreeCollapseToggleModel
                          {
                              Id = p.Id,
                              Text = p.Name,
                              Url = p.Url,
                              ParentId = string.IsNullOrEmpty(p.ParentId) ? p.ModuleId : p.ParentId,
                              SortCode = p.SortCode,
                              Icon = "touch_app",
                              CanToggle = true,
                              Disabled = false,
                              Expend = true,
                              ToggleMark = authorizedButtonIds.Contains(p.Id)
                          };

            var moduleClaims = from p in _context.Sys_ModuleClaims.AsNoTracking().Where(x => x.DeleteMark != true)
                               select new TreeCollapseToggleModel
                               {
                                   Id = p.Id,
                                   Text = p.Name,
                                   Url = p.Description,
                                   ParentId = p.ModuleId,
                                   SortCode = p.SortCode,
                                   Icon = "lightbulb_outline",
                                   CanToggle = true,
                                   Disabled = false,
                                   Expend = true,
                                   ToggleMark = authorizedModuleClaimIds.Contains(p.Id)
                               };

            var buttonClaims = from p in _context.Sys_ModuleButtonClaims.AsNoTracking().Where(x => x.DeleteMark != true)
                               select new TreeCollapseToggleModel
                               {
                                   Id = p.Id,
                                   Text = p.Name,
                                   Url = p.Description,
                                   ParentId = p.ButtonId,
                                   SortCode = p.SortCode,
                                   Icon = "lightbulb_outline",
                                   CanToggle = true,
                                   Disabled = false,
                                   Expend = true,
                                   ToggleMark = authorizedButtonClaimIds.Contains(p.Id)
                               };

            return modules.Union(buttons).Union(moduleClaims).Union(buttonClaims).ToList();

        }

        /// <summary>
        /// 根据用户名获取关联岗位列表
        /// </summary>
        /// <param name="userid"></param>
        /// <returns></returns>
        public List<PostModel> GetPostsByPrincipal(System.Security.Claims.ClaimsPrincipal principal)
        {
            var user = GetUserAsync(principal).Result;
            if (user == null)
                return null;

            var postIds = from p in _context.Sys_UserPosts.AsNoTracking().Where(x => x.UserId == user.Id)
                          select p.PostId;

            var data = _context.Sys_Posts.AsNoTracking().Where(x => x.DeleteMark != true && postIds.Contains(x.Id)).OrderBy(x => x.SortCode);
            return data.ToList();
        }

        public  ModelResult Remove(string id, string deleterId)
        {
            var user = FindByIdAsync(id).Result;
            if (user == null)
                return Common.ErrorResult("未找到指定id的用户");
            
            user.DeleteMark = true;
            user.DeleteTime = DateTime.Now;
            user.DeleteUserId = deleterId;
            user.UserName = user.UserName + "_bak";
            user.NormalizedUserName = user.NormalizedUserName + "_BAK";

            var result = UpdateAsync(user).Result;
            if (result.Succeeded)
            {
                return Common.SuccessResult();
            }
            else
            {
                var sb = new StringBuilder();
                foreach (var err in result.Errors)
                {
                    sb.Append(err.Description + "<br>");
                }
                return Common.ErrorResult(sb.ToString());
            }
        }


        public IList<UserModel> GetPagedList(string keyword, UserType? userType, Pagination pagination)
        {
            var exp = ExtLinq.True<UserModel>();
            exp = exp.And(x => x.DeleteMark != true);
            if (!string.IsNullOrEmpty(keyword))
            {
                exp = exp.And(x => x.RealName.Contains(keyword)||x.UserName.Contains(keyword));
            }
            if (userType.HasValue)
            {
                exp = exp.And(x => x.UserType == userType.Value);
            }
            var data = _context.FindList(exp, pagination);
            return data;
        }
    }
}
